package dkbta.processing.data;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import dkbta.ontology.Ontology;
import dkbta.ontology.elements.ElementDef;
import dkbta.processing.ActionTimer;
import dkbta.processing.Patient;
import dkbta.processing.Patients;
import dkbta.util.Log;

/**
 * A thread that listens on the unit's data port for incoming connections from other units
 * with data. <br>
 * <br>
 * Note that this is Daemon thread, i.e. it will not keep the unit from shutting down even
 * if there is an active connection. This however will not interfere with the receiving of
 * required remote elements as they will be wrapped by <b>primary</b> tasks and the work
 * queue (which is run from a non-daemon thread) will not finish before they are all
 * received.
 * 
 * @author Uri Kanonov
 */
public class DataServer extends Thread{
	/** The data port */
	private final int _dataPort;

	/** A reference to the ontology */
	private final Ontology _ontology;

	/** A reference to the patient container */
	private final Patients _patients;

	/* 
	 * CHANGE-1 Arkady 27.05.10
	 * In order to Interrupd Thread
	 */
	private ServerSocket _srv;
	private volatile boolean _shouldFinish;
	
	/**
	 * Initializes (but not starts) the data server
	 * 
	 * @param patients A reference to the patient container
	 * @param ontology A reference to the ontology
	 * @param dataPort The data port (port on which to listen)
	 */
	public DataServer(Patients patients, Ontology ontology, int dataPort){
		super("Data server");
		_patients = patients;
		_ontology = ontology;
		_dataPort = dataPort;
		setDaemon(true);
		_shouldFinish = false;
	}

	@Override
	public void run(){
		final Log log = Log.getLogger();
		final ActionTimer actionTimer = ActionTimer.getInstance();
		try{
			_srv = new ServerSocket(_dataPort);
			while (!_shouldFinish){
				final Socket socket = _srv.accept();
				log.finer("Receiving data from: " + socket.getRemoteSocketAddress());

				// Reading the batch
				new Thread(){
					@Override
					public void run(){
						try{
							// Reading the data from the socket
							int dataReceivingActionId = actionTimer.actionStart();
							InputStreamDataReader isdr = new InputStreamDataReader(
									_ontology, socket.getInputStream(), _patients, true,
									InputStreamDataReader.ValidElements.REMOTE_ELEMENTS);
							isdr.read();

							// Noting that the received element has been computed
							ElementDef elementDef = isdr.getElement();
							Patient patient = isdr.getPatient();
							if (patient == null || elementDef == null){
								throw new DataException(
										"Couldn't resolve patient/element from received data");
							}
							patient.addComputedElement(elementDef);

							actionTimer.actionEnd(dataReceivingActionId, patient
									.getPatientId(), elementDef.getName(), elementDef
									.getElementType(), ActionTimer.Action.RECEIVING_DATA);
						}catch(Exception e){
							log.warning("Unable to read data from a connection", e);
						}
					}
				}.start();
			}
		}catch(Throwable t){
			//Log.getLogger().severe("Data server thread died, shutting down unit", t);
//			Log.getLogger().severe("Data server thread died, shutting down unit");
			//System.exit(1);
		}
//		Log.getLogger().severe("DataServer finished");
	}
	
	public synchronized void waitForFinish() throws InterruptedException{
		_shouldFinish = true;
		try {
			_srv.close();
			interrupt();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
		}
		join();
	}
	
}
